"""
告警规则引擎
实现可配置的告警规则管理、告警级别和优先级分类、告警去重和聚合处理
"""
import asyncio
import logging
import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Any, Set
from uuid import uuid4
from collections import defaultdict, deque
import hashlib
import re

from schemas.alert_management import (
    AlertRule, AlertEvent, AlertRuleCondition, AlertLevel, AlertStatus,
    AlertCategory, RuleConditionOperator, AlertRuleCreateRequest,
    AlertRuleUpdateRequest
)

logger = logging.getLogger(__name__)


class RuleConditionEvaluator:
    """规则条件评估器"""
    
    @staticmethod
    def evaluate_condition(condition: AlertRuleCondition, data: Dict[str, Any]) -> bool:
        """评估单个条件"""
        try:
            field_value = RuleConditionEvaluator._get_field_value(data, condition.field)
            condition_value = condition.value
            operator = condition.operator
            
            if field_value is None:
                return False
            
            # 数值比较
            if operator == RuleConditionOperator.EQUALS:
                return field_value == condition_value
            elif operator == RuleConditionOperator.NOT_EQUALS:
                return field_value != condition_value
            elif operator == RuleConditionOperator.GREATER_THAN:
                return float(field_value) > float(condition_value)
            elif operator == RuleConditionOperator.LESS_THAN:
                return float(field_value) < float(condition_value)
            elif operator == RuleConditionOperator.GREATER_EQUAL:
                return float(field_value) >= float(condition_value)
            elif operator == RuleConditionOperator.LESS_EQUAL:
                return float(field_value) <= float(condition_value)
            
            # 字符串比较
            elif operator == RuleConditionOperator.CONTAINS:
                return str(condition_value) in str(field_value)
            elif operator == RuleConditionOperator.NOT_CONTAINS:
                return str(condition_value) not in str(field_value)
            
            # 列表比较
            elif operator == RuleConditionOperator.IN:
                return field_value in condition_value
            elif operator == RuleConditionOperator.NOT_IN:
                return field_value not in condition_value
            
            return False
            
        except Exception as e:
            logger.error(f"条件评估失败: {e}")
            return False
    
    @staticmethod
    def _get_field_value(data: Dict[str, Any], field_path: str) -> Any:
        """获取嵌套字段值"""
        try:
            keys = field_path.split('.')
            value = data
            for key in keys:
                if isinstance(value, dict):
                    value = value.get(key)
                else:
                    return None
            return value
        except Exception:
            return None


class AlertDeduplicator:
    """告警去重器"""
    
    def __init__(self, window_minutes: int = 5):
        self.window_minutes = window_minutes
        self.alert_hashes: Dict[str, datetime] = {}
        self.cleanup_interval = 60  # 清理间隔(秒)
        self.last_cleanup = datetime.now()
    
    def is_duplicate(self, alert_data: Dict[str, Any]) -> bool:
        """检查是否为重复告警"""
        alert_hash = self._generate_alert_hash(alert_data)
        current_time = datetime.now()
        
        # 定期清理过期的哈希
        if (current_time - self.last_cleanup).seconds > self.cleanup_interval:
            self._cleanup_expired_hashes(current_time)
        
        # 检查是否存在相同的告警
        if alert_hash in self.alert_hashes:
            last_time = self.alert_hashes[alert_hash]
            if (current_time - last_time).total_seconds() < self.window_minutes * 60:
                return True
        
        # 记录新的告警哈希
        self.alert_hashes[alert_hash] = current_time
        return False
    
    def _generate_alert_hash(self, alert_data: Dict[str, Any]) -> str:
        """生成告警哈希"""
        # 提取关键字段用于去重
        key_fields = {
            'rule_id': alert_data.get('rule_id'),
            'category': alert_data.get('category'),
            'location': alert_data.get('location'),
            'camera_id': alert_data.get('camera_id'),
            'classroom_id': alert_data.get('classroom_id'),
            'teacher_id': alert_data.get('teacher_id')
        }
        
        # 过滤None值
        key_fields = {k: v for k, v in key_fields.items() if v is not None}
        
        # 生成哈希
        hash_string = json.dumps(key_fields, sort_keys=True)
        return hashlib.md5(hash_string.encode()).hexdigest()
    
    def _cleanup_expired_hashes(self, current_time: datetime):
        """清理过期的哈希"""
        expired_hashes = []
        cutoff_time = current_time - timedelta(minutes=self.window_minutes * 2)
        
        for alert_hash, timestamp in self.alert_hashes.items():
            if timestamp < cutoff_time:
                expired_hashes.append(alert_hash)
        
        for alert_hash in expired_hashes:
            del self.alert_hashes[alert_hash]
        
        self.last_cleanup = current_time


class AlertAggregator:
    """告警聚合器"""
    
    def __init__(self, aggregation_window: int = 60):
        self.aggregation_window = aggregation_window  # 聚合窗口(秒)
        self.pending_alerts: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
        self.aggregation_rules = {
            AlertCategory.CROWD_SAFETY: self._aggregate_crowd_alerts,
            AlertCategory.BEHAVIOR_SAFETY: self._aggregate_behavior_alerts,
            AlertCategory.HYGIENE_MANAGEMENT: self._aggregate_hygiene_alerts,
            AlertCategory.ENERGY_MANAGEMENT: self._aggregate_energy_alerts,
            AlertCategory.CLASSROOM_QUALITY: self._aggregate_classroom_alerts,
            AlertCategory.TEACHING_QUALITY: self._aggregate_teaching_alerts
        }
    
    def add_alert_for_aggregation(self, alert_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """添加告警到聚合队列"""
        category = alert_data.get('category')
        aggregation_key = self._generate_aggregation_key(alert_data)
        
        # 添加到待聚合列表
        self.pending_alerts[aggregation_key].append({
            **alert_data,
            'received_at': datetime.now()
        })
        
        # 检查是否需要触发聚合
        if self._should_trigger_aggregation(aggregation_key):
            return self._create_aggregated_alert(aggregation_key)
        
        return None
    
    def _generate_aggregation_key(self, alert_data: Dict[str, Any]) -> str:
        """生成聚合键"""
        key_parts = [
            alert_data.get('category', ''),
            alert_data.get('location', ''),
            alert_data.get('camera_id', ''),
            alert_data.get('classroom_id', '')
        ]
        return '|'.join(filter(None, key_parts))
    
    def _should_trigger_aggregation(self, aggregation_key: str) -> bool:
        """判断是否应该触发聚合"""
        alerts = self.pending_alerts[aggregation_key]
        if len(alerts) < 2:
            return False
        
        # 检查时间窗口
        now = datetime.now()
        first_alert_time = alerts[0]['received_at']
        
        return (now - first_alert_time).total_seconds() >= self.aggregation_window
    
    def _create_aggregated_alert(self, aggregation_key: str) -> Dict[str, Any]:
        """创建聚合告警"""
        alerts = self.pending_alerts[aggregation_key]
        if not alerts:
            return None
        
        # 获取聚合规则
        category = alerts[0].get('category')
        aggregation_func = self.aggregation_rules.get(category, self._default_aggregation)
        
        # 执行聚合
        aggregated_alert = aggregation_func(alerts)
        
        # 清空待聚合列表
        self.pending_alerts[aggregation_key] = []
        
        return aggregated_alert
    
    def _aggregate_crowd_alerts(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """聚合人流安全告警"""
        base_alert = alerts[0]
        alert_count = len(alerts)
        
        # 计算平均密度
        densities = [alert.get('source_data', {}).get('density', 0) for alert in alerts]
        avg_density = sum(densities) / len(densities) if densities else 0
        
        return {
            **base_alert,
            'alert_id': str(uuid4()),
            'title': f"人流密度异常聚合告警 ({alert_count}次)",
            'message': f"在{self.aggregation_window}秒内检测到{alert_count}次人流密度异常，平均密度: {avg_density:.2f}",
            'source_data': {
                'aggregated_count': alert_count,
                'avg_density': avg_density,
                'max_density': max(densities) if densities else 0,
                'original_alerts': [alert['alert_id'] for alert in alerts if 'alert_id' in alert]
            },
            'level': self._determine_aggregated_level([alert.get('level') for alert in alerts])
        }
    
    def _aggregate_behavior_alerts(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """聚合行为安全告警"""
        base_alert = alerts[0]
        alert_count = len(alerts)
        
        behavior_types = [alert.get('source_data', {}).get('behavior_type', '') for alert in alerts]
        unique_behaviors = list(set(filter(None, behavior_types)))
        
        return {
            **base_alert,
            'alert_id': str(uuid4()),
            'title': f"行为安全异常聚合告警 ({alert_count}次)",
            'message': f"在{self.aggregation_window}秒内检测到{alert_count}次行为异常，涉及行为: {', '.join(unique_behaviors)}",
            'source_data': {
                'aggregated_count': alert_count,
                'behavior_types': unique_behaviors,
                'original_alerts': [alert['alert_id'] for alert in alerts if 'alert_id' in alert]
            },
            'level': self._determine_aggregated_level([alert.get('level') for alert in alerts])
        }
    
    def _aggregate_hygiene_alerts(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """聚合环境卫生告警"""
        base_alert = alerts[0]
        alert_count = len(alerts)
        
        cleanliness_scores = [alert.get('source_data', {}).get('cleanliness_score', 0) for alert in alerts]
        avg_score = sum(cleanliness_scores) / len(cleanliness_scores) if cleanliness_scores else 0
        
        return {
            **base_alert,
            'alert_id': str(uuid4()),
            'title': f"环境卫生异常聚合告警 ({alert_count}次)",
            'message': f"在{self.aggregation_window}秒内检测到{alert_count}次卫生异常，平均清洁度: {avg_score:.2f}",
            'source_data': {
                'aggregated_count': alert_count,
                'avg_cleanliness_score': avg_score,
                'min_cleanliness_score': min(cleanliness_scores) if cleanliness_scores else 0,
                'original_alerts': [alert['alert_id'] for alert in alerts if 'alert_id' in alert]
            },
            'level': self._determine_aggregated_level([alert.get('level') for alert in alerts])
        }
    
    def _aggregate_energy_alerts(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """聚合能耗管理告警"""
        base_alert = alerts[0]
        alert_count = len(alerts)
        
        return {
            **base_alert,
            'alert_id': str(uuid4()),
            'title': f"能耗异常聚合告警 ({alert_count}次)",
            'message': f"在{self.aggregation_window}秒内检测到{alert_count}次能耗异常",
            'source_data': {
                'aggregated_count': alert_count,
                'original_alerts': [alert['alert_id'] for alert in alerts if 'alert_id' in alert]
            },
            'level': self._determine_aggregated_level([alert.get('level') for alert in alerts])
        }
    
    def _aggregate_classroom_alerts(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """聚合课堂质量告警"""
        base_alert = alerts[0]
        alert_count = len(alerts)
        
        attention_scores = [alert.get('source_data', {}).get('attention_score', 0) for alert in alerts]
        avg_attention = sum(attention_scores) / len(attention_scores) if attention_scores else 0
        
        return {
            **base_alert,
            'alert_id': str(uuid4()),
            'title': f"课堂质量异常聚合告警 ({alert_count}次)",
            'message': f"在{self.aggregation_window}秒内检测到{alert_count}次课堂质量异常，平均专注度: {avg_attention:.2f}",
            'source_data': {
                'aggregated_count': alert_count,
                'avg_attention_score': avg_attention,
                'original_alerts': [alert['alert_id'] for alert in alerts if 'alert_id' in alert]
            },
            'level': self._determine_aggregated_level([alert.get('level') for alert in alerts])
        }
    
    def _aggregate_teaching_alerts(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """聚合教学质量告警"""
        base_alert = alerts[0]
        alert_count = len(alerts)
        
        return {
            **base_alert,
            'alert_id': str(uuid4()),
            'title': f"教学质量异常聚合告警 ({alert_count}次)",
            'message': f"在{self.aggregation_window}秒内检测到{alert_count}次教学质量异常",
            'source_data': {
                'aggregated_count': alert_count,
                'original_alerts': [alert['alert_id'] for alert in alerts if 'alert_id' in alert]
            },
            'level': self._determine_aggregated_level([alert.get('level') for alert in alerts])
        }
    
    def _default_aggregation(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """默认聚合规则"""
        base_alert = alerts[0]
        alert_count = len(alerts)
        
        return {
            **base_alert,
            'alert_id': str(uuid4()),
            'title': f"聚合告警 ({alert_count}次)",
            'message': f"在{self.aggregation_window}秒内检测到{alert_count}次相似告警",
            'source_data': {
                'aggregated_count': alert_count,
                'original_alerts': [alert['alert_id'] for alert in alerts if 'alert_id' in alert]
            },
            'level': self._determine_aggregated_level([alert.get('level') for alert in alerts])
        }
    
    def _determine_aggregated_level(self, levels: List[str]) -> str:
        """确定聚合告警级别"""
        level_priority = {
            AlertLevel.CRITICAL: 5,
            AlertLevel.HIGH: 4,
            AlertLevel.MEDIUM: 3,
            AlertLevel.LOW: 2,
            AlertLevel.INFO: 1
        }
        
        max_priority = 0
        result_level = AlertLevel.INFO
        
        for level in levels:
            if level and level in level_priority:
                priority = level_priority[level]
                if priority > max_priority:
                    max_priority = priority
                    result_level = level
        
        return result_level


class AlertRulesEngine:
    """告警规则引擎"""
    
    def __init__(self):
        self.rules: Dict[str, AlertRule] = {}
        self.rule_cooldowns: Dict[str, datetime] = {}
        self.rule_counters: Dict[str, deque] = defaultdict(lambda: deque(maxlen=100))
        self.condition_evaluator = RuleConditionEvaluator()
        self.deduplicator = AlertDeduplicator()
        self.aggregator = AlertAggregator()
        
        # 性能统计
        self.stats = {
            'rules_evaluated': 0,
            'alerts_generated': 0,
            'alerts_deduplicated': 0,
            'alerts_aggregated': 0
        }
    
    async def add_rule(self, rule_request: AlertRuleCreateRequest) -> AlertRule:
        """添加告警规则"""
        rule_id = str(uuid4())
        rule = AlertRule(
            rule_id=rule_id,
            **rule_request.dict()
        )
        
        self.rules[rule_id] = rule
        logger.info(f"添加告警规则: {rule.name} ({rule_id})")
        
        return rule
    
    async def update_rule(self, rule_id: str, rule_request: AlertRuleUpdateRequest) -> Optional[AlertRule]:
        """更新告警规则"""
        if rule_id not in self.rules:
            return None
        
        rule = self.rules[rule_id]
        update_data = rule_request.dict(exclude_unset=True)
        
        for field, value in update_data.items():
            setattr(rule, field, value)
        
        rule.updated_at = datetime.now()
        
        logger.info(f"更新告警规则: {rule.name} ({rule_id})")
        return rule
    
    async def delete_rule(self, rule_id: str) -> bool:
        """删除告警规则"""
        if rule_id in self.rules:
            rule = self.rules.pop(rule_id)
            # 清理相关数据
            if rule_id in self.rule_cooldowns:
                del self.rule_cooldowns[rule_id]
            if rule_id in self.rule_counters:
                del self.rule_counters[rule_id]
            
            logger.info(f"删除告警规则: {rule.name} ({rule_id})")
            return True
        return False
    
    async def get_rule(self, rule_id: str) -> Optional[AlertRule]:
        """获取告警规则"""
        return self.rules.get(rule_id)
    
    async def list_rules(self, category: Optional[AlertCategory] = None, 
                        enabled_only: bool = True) -> List[AlertRule]:
        """列出告警规则"""
        rules = list(self.rules.values())
        
        if enabled_only:
            rules = [rule for rule in rules if rule.enabled]
        
        if category:
            rules = [rule for rule in rules if rule.category == category]
        
        return rules
    
    async def evaluate_data(self, data: Dict[str, Any]) -> List[AlertEvent]:
        """评估数据并生成告警"""
        generated_alerts = []
        
        for rule in self.rules.values():
            if not rule.enabled:
                continue
            
            self.stats['rules_evaluated'] += 1
            
            # 检查冷却时间
            if not self._check_cooldown(rule.rule_id):
                continue
            
            # 检查频率限制
            if not self._check_rate_limit(rule.rule_id):
                continue
            
            # 评估规则条件
            if self._evaluate_rule_conditions(rule, data):
                alert_event = await self._create_alert_event(rule, data)
                
                # 去重检查
                if self.deduplicator.is_duplicate(alert_event.dict()):
                    self.stats['alerts_deduplicated'] += 1
                    continue
                
                # 聚合检查
                aggregated_alert = self.aggregator.add_alert_for_aggregation(alert_event.dict())
                if aggregated_alert:
                    # 使用聚合告警
                    alert_event = AlertEvent(**aggregated_alert)
                    self.stats['alerts_aggregated'] += 1
                
                generated_alerts.append(alert_event)
                self.stats['alerts_generated'] += 1
                
                # 更新冷却时间和计数器
                self._update_rule_tracking(rule.rule_id)
        
        return generated_alerts
    
    def _evaluate_rule_conditions(self, rule: AlertRule, data: Dict[str, Any]) -> bool:
        """评估规则条件"""
        if not rule.conditions:
            return False
        
        # 所有条件都必须满足(AND逻辑)
        for condition in rule.conditions:
            if not self.condition_evaluator.evaluate_condition(condition, data):
                return False
        
        return True
    
    async def _create_alert_event(self, rule: AlertRule, data: Dict[str, Any]) -> AlertEvent:
        """创建告警事件"""
        alert_id = str(uuid4())
        
        # 生成告警标题和消息
        title = f"{rule.name}"
        message = rule.description or f"规则 {rule.name} 被触发"
        
        # 提取位置信息
        location = data.get('location') or data.get('camera_location')
        camera_id = data.get('camera_id')
        classroom_id = data.get('classroom_id')
        teacher_id = data.get('teacher_id')
        
        return AlertEvent(
            alert_id=alert_id,
            rule_id=rule.rule_id,
            title=title,
            message=message,
            category=rule.category,
            level=rule.level,
            source_data=data,
            location=location,
            camera_id=camera_id,
            classroom_id=classroom_id,
            teacher_id=teacher_id,
            tags=rule.tags.copy()
        )
    
    def _check_cooldown(self, rule_id: str) -> bool:
        """检查冷却时间"""
        if rule_id not in self.rule_cooldowns:
            return True
        
        rule = self.rules[rule_id]
        last_trigger = self.rule_cooldowns[rule_id]
        cooldown_seconds = rule.cooldown_seconds
        
        return (datetime.now() - last_trigger).total_seconds() >= cooldown_seconds
    
    def _check_rate_limit(self, rule_id: str) -> bool:
        """检查频率限制"""
        rule = self.rules[rule_id]
        max_alerts = rule.max_alerts_per_hour
        
        if max_alerts <= 0:
            return True
        
        # 清理一小时前的记录
        now = datetime.now()
        one_hour_ago = now - timedelta(hours=1)
        
        counter = self.rule_counters[rule_id]
        while counter and counter[0] < one_hour_ago:
            counter.popleft()
        
        return len(counter) < max_alerts
    
    def _update_rule_tracking(self, rule_id: str):
        """更新规则跟踪信息"""
        now = datetime.now()
        self.rule_cooldowns[rule_id] = now
        self.rule_counters[rule_id].append(now)
    
    def get_engine_stats(self) -> Dict[str, Any]:
        """获取引擎统计信息"""
        return {
            **self.stats,
            'total_rules': len(self.rules),
            'enabled_rules': len([r for r in self.rules.values() if r.enabled]),
            'rules_in_cooldown': len(self.rule_cooldowns),
            'deduplication_window': self.deduplicator.window_minutes,
            'aggregation_window': self.aggregator.aggregation_window
        }